﻿using MubiSDK.LoggingLib.Core.Interface;
using MubiSDK.LoggingLib.Helper;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MubiSDK.LoggingLib.Core.Sinks
{
    /// <summary>
    /// 提供将日志消息写入指定文件的实现
    /// 实现了 <see cref="ILogSink"/> 和 <see cref="IDisposable"/> 接口
    /// </summary>
    public class FileLogSink : ILogSink, IDisposable
    {
        /// <summary>
        /// 用于写入日志文件的流写入器
        /// </summary>
        private readonly StreamWriter _writer = null;

        /// <summary>
        /// 标记资源是否已被释放，用于实现标准的 Dispose 模式
        /// </summary>
        private bool _disposed = false;

        /// <summary>
        /// 初始化 <see cref="FileLogSink"/> 类的新实例，并打开（或创建）指定的日志文件
        /// </summary>
        /// <param name="filePath">日志文件的完整路径</param>
        /// <exception cref="ArgumentNullException">当 <paramref name="filePath"/> 为 null 或空字符串时抛出</exception>
        /// <exception cref="InvalidOperationException">当无法创建文件目录或初始化文件流时抛出</exception>
        public FileLogSink(string filePath)
        {
            if (string.IsNullOrEmpty(filePath))
            {
                throw new ArgumentNullException(nameof(filePath), "目标日志文件路径不能为空。");
            }

            // 确保目标目录被创建
            string directory = FileHelper.GetFileDirectory(filePath);
            if (!FileHelper.CreateDirectory(directory))
            {
                throw new InvalidOperationException($"无法创建或访问日志文件目录：{directory}。请检查权限或磁盘空间。");
            }

            try
            {
                // 以追加模式打开打开(创建)目标文件，并指定写入编码为UTF-8
                _writer = new StreamWriter(path: filePath, append: true, encoding: Encoding.UTF8)
                {
                    // 设置自动刷新去报日志及时写入
                    AutoFlush = true
                };
            }
            catch(Exception ex) when (ex is DirectoryNotFoundException || 
                                    ex is UnauthorizedAccessException || 
                                    ex is IOException)
            {
                // 包装并抛出更具体的异常
                throw new InvalidOperationException($"创建日志文件写入器失败。文件路径：{filePath}", ex);
            }
        }

        /// <summary>
        /// 将格式化后的日志消息写入文件
        /// </summary>
        /// <param name="formattedMessage">已格式化的日志消息字符串</param>
        /// <exception cref="ObjectDisposedException">当方法在对象已被释放后调用时抛出</exception>
        /// <remarks>
        /// 此方法会捕获并忽略IO异常，以确保日志写入失败不会影响应用程序主流程
        /// 在Debug模式下，异常详情会输出到调试控制台
        /// </remarks>
        public void Write(string formattedMessage)
        {
            if (_disposed)
            {
                throw new ObjectDisposedException(nameof(FileLogSink));
            }

            try
            {
                _writer.WriteLine(formattedMessage);
            }
            catch (IOException ex)
            {
                // 日志组件自身应具有高容错性。此处选择捕获并记录异常，而非抛出。
                // 在调试时，可通过输出查看问题；在生产环境中，可考虑扩展为触发一个失败事件。
                System.Diagnostics.Debug.WriteLine($"日志写入失败: {ex.Message}");
            }
        }

        /// <summary>
        /// 释放该对象占用的所有资源
        /// </summary>
        public void Dispose()
        {
            Dispose(true);              // 手动释放托管资源
            GC.SuppressFinalize(this);  // 通知垃圾回收器不再需要调用终结器
        }

        /// <summary>
        /// 执行实际的资源释放逻辑
        /// </summary>
        /// <param name="disposing">
        /// <para>若为 true，表示是显式调用Dispose()所致，应释放托管和非托管资源</para>
        /// <para>若为 false，表示是终结器调用，只能释放非托管资源</para>
        /// </param>
        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    // 释放托管资源
                    _writer?.Dispose();
                }
                
                // 此处可释放非托管资源
                // ...

                // 资源释放完成
                _disposed = true;
            }
        }

        /// <summary>
        /// 终结器。作为资源释放的最后保障，防止因未调用Dispose而导致资源泄漏
        /// </summary>
        ~FileLogSink()
        {
            Dispose(false);
        }
    }
}
